import java.awt.Color;
import java.util.ArrayList;
import java.io.*;

public class CScoutThread extends Thread{
  protected Conrad _parent;
  protected CBoard _board;
  protected Color _color, _hisColor;
  protected final int NINF = Integer.MIN_VALUE + 1;
  protected final int PINF = Integer.MAX_VALUE;
  protected final int _maxDepth = 24;   // reasonable

  protected int evaluate(CBoard board){
    final double[] cMaterial = new double[]{
      5000., -900., 200., 100., 100., 200., -900., 5000.,
      -900.,-1000., 500., 100., 100., 500.,-1000., -900.,
       200.,  500., 100., 100., 100., 100.,  500.,  200.,
       100.,  100., 100., 100., 100., 100.,  100.,  100.,
       100.,  100., 100., 100., 100., 100.,  100.,  100.,
       200.,  500., 100., 100., 100., 100.,  500.,  200.,
      -900.,-1000., 500., 100., 100., 500.,-1000., -900.,
      5000., -900., 200., 100., 100., 200., -900., 5000.
    };
    double score = 0;
    int pieces = 0, piecesUs = 0, piecesThem = 0;
    boolean mustPass = true;
    double material = 0., eMaterial = 0., mobility = 0., eMobility = 0.;

    Color us = board._curPlayer, enemy = board._otherPlayer;
    Color tmp;

    // Loop through each square, counting our tiles as well as
    // mobility. Should be fairly self-explanatory
    for (int i = 0; i < 64; i++){
      tmp = board._board[i];
      if (tmp == us){
        material += cMaterial[i];
        pieces++; piecesUs++;
      }
      else if(tmp == enemy){
        eMaterial += cMaterial[i];
        pieces++; piecesThem++;
      }

      // else there's no one on this square
      else{
        if (board._curValidMoves[i]){     // us == _curPlayer
          mobility += cMaterial[i];
          mustPass = false;
        }
        else if (board._otherValidMoves[i]){
          eMobility += cMaterial[i];
        }
      }
    }

    // Score based on part-of-game
    // I don't know where the numbers come from; might try mutating
    // them in evolutionfights later
    if (pieces < 30){
      // Beginning game, material is important, while mobility less so.
      // But we really don't want to pass this early on.
      score += (material - eMaterial)*3;
      score += (mobility - eMobility)*0.5;
      score -= mustPass? 2000000. : 0;
    }
    else if (pieces < 42){
      // Midgame, up the importance of mobility some, decrease that of
      // material... passing is semi-ok now
      score += (material - eMaterial)*1.8;
      score += (mobility - eMobility)*0.9;
      score -= mustPass? 1000000. : 0;
    }
    else if (pieces < 48){
      if (material < eMaterial) score -= 10000;
      score += (mobility - eMobility)*3;
      score -= mustPass? 1100000. : 0;
    }
    else{
      // End game, last 18 or fewer moves. Mobility counts for a lot here.
      // Check to see if this node constitutes either our victory or
      // humiliation. Compensate score accordingly.
      if (mustPass && board.isGameOver){
        if (piecesUs > piecesThem){
          mustPass = false;
          score += PINF/10;
        }
        else score -= PINF/10;
      }

      if (material < eMaterial) score -= 10000;
      score += (mobility - eMobility)*5;
      score -= mustPass? 1200000. : 0;
    }

    return (int)score;
  }

  public CScoutThread(Conrad parent, CBoard board, Color color,
      Color hisColor){
    _parent   = parent;
    _color    = color;
    _hisColor = hisColor;
    _board    = board;

    try{
      BufferedWriter out = new BufferedWriter(new FileWriter("runlog",
            true));
      out.write("--nt\n");
      out.close();
    }
    catch (IOException ioe){
    }
  }

  public void run(){
    // iteratively deeply search each set of valid moves so we can
    // efficiently negascout
    ArrayList<Integer> moves = new ArrayList<Integer>();
    for (int i = 0; i < 64; i++)
      if (_board._curValidMoves[i]) moves.add(i);
    int idx, depth;
    Integer bM;

    for (depth = 1; depth < _maxDepth; depth ++){
      try{
        BufferedWriter out = new BufferedWriter(new FileWriter("runlog",
              true));
        out.write("++ ");
        out.write(((Integer)depth).toString());
        out.write("\n");
        out.close();
      }
      catch (IOException ioe){
      }

      bM = topNegaScout(depth, moves);
      _parent.bestMove = bM;

      try{
        BufferedWriter out = new BufferedWriter(new FileWriter("runlog",
              true));
        out.write("       -> r, c => [");
        out.write(((Integer)((bM>>3) + 1)).toString());
        out.write(", ");
        out.write(((Integer)((bM&7) + 1)).toString());
        out.write("]\n");
        out.close();
      }
      catch (IOException ioe){
      }

      // re-order moves best first
      if (moves.size() > 0 && (idx = moves.indexOf(bM)) != 0){
        moves.set(idx, moves.get(0));
        moves.set(0,   bM);
      }
    }
  }

  protected int negaScout(CBoard node, int depth, int a, int B){
    int b, v;
    CBoard child;
    boolean first = true;

    dumpBoard(node);

    if (node.isGameOver || depth == 0)
      return evaluate(node);

    ArrayList<Integer> playerMoves = new ArrayList<Integer>();
    for (int i = 0; i < 64; i++)
      if (node._curValidMoves[i]) playerMoves.add(i);

    if (playerMoves.size() == 0)
      return evaluate(node);

    b = B;
    for (Integer move : playerMoves){
      child = new CBoard(node, move);

      v = -negaScout(child, depth - 1, -b, -a);
      if (a < v && v < B && !first)
        v = -negaScout(child, depth - 1, -B, -v);
      a = (a > v)? a : v;
      if (a >= B) return a;
      b = a + 1;
    }
    return a;
  }

  protected Integer topNegaScout(int depth, ArrayList<Integer> moves){
    int a = NINF, B = PINF, v, b = PINF;
    boolean firstChild = true;
    CBoard child;
    Integer bestMove = 64;
    int bestScore = NINF;

    for (Integer move : moves){
      child = new CBoard(_board, move);

      v = -negaScout(child, depth - 1, -b, -a);
      if (a < v && v < B && !firstChild)
        v = -negaScout(child, depth - 1, -B, -v);
      a = (a > v)? a : v;

      if (a > bestScore){
        bestScore = a;
        bestMove  = move;
      }

      b = a + 1;
      firstChild = false;
    }

    return bestMove;
  }

  public void dumpBoard(CBoard root){
    System.out.println(" 12345678");
    for (int i = 0; i < 64; i++){
      if ((i&7) == 0) System.out.print("" + ((i >> 3)+1));
      if (root._curValidMoves[i])
        System.out.print("W");
//    else if (root._otherValidMoves[i])
//      System.out.print("B");
      else if (root._board[i] == root._curPlayer)
        System.out.print("o");
      else if (root._board[i] == root._otherPlayer)
        System.out.print("X");
      else
        System.out.print(" ");
      if (i % 8 == 7) System.out.println("");
    }
  }

}
